// This package defines an API for a federated name service.
//
// documentation outline.
//
// subtrees + mountprotocol
// subtrees may or may not implement mp
// subtress are typed
// test with a type assertion
// subtrees may be used independently of global name space
//
// local runtime provides an instance of a mountprotocol that is the interface
// to the global service
//
// define structure of names, regexes etc.
// will likely need some additional operations for these - e.g.
// 'resolve(wildcard) -> multiple names, each of which can be looked up'.
// - i.e. calling resolve may lead to 'export' being called internally to create
// the names. This doesn't happen with lookup.
//
// need to define storage semantics for names somehow...
//
// have a separate class for operating, validating name strings
//
// use of names and dss's must be clean+easy
package names

import (
	"errors"
	"reflect"
)

var (
	ErrInvalidInterface  = errors.New("This interface is not supported by this subtree.")
	ErrInterfaceTooLarge = errors.New("This interface instance is too large to be stored in this subtree.")
	ErrNoSuchName        = errors.New("Name doesn't exist")
	ErrNameExists        = errors.New("Name already exists")
	ErrTooLarge          = errors.New("Value is too large to store")
	ErrInvalidOptions    = errors.New("Invalid/unsupported options requested")
)

// A BulkReader is for reading multiple name, value pairs from a
// SubTree. The Values will always be an array of arbitrary types and
// the names an array of strings.
// BulkReader's can return constructed or otherwise manipulated types
// and values derived from the SubTree's data.
type BulkReader interface {
	ReadN(start string, num uint64) (uint64, error)
	Names() []string
	Values() interface{}
}

type SubTree interface {
	// All names are relative to the root of this subtree and must be
	// 'clean' - name.Clean() can be used to ensure that this is the case.

	// Export (i.e. create) a new name.
	// Export must return an ErrNameExists if the name already exists
	// and overwrite is false.
	Export(name string, val interface{}, overwrite bool) error

	// Lookup an existing to obtain its associated value
	Lookup(name string) (interface{}, error)

	// Delete an existing name and its associated value
	Delete(name string) error

	// Create a BulkReader instance.
	// Options allows for different scanner implementations/strategies
	// to be requested. nil may be returned if the BulkReader API is not
	// supported.
	BulkReader(options string) (BulkReader, error)

	Type() reflect.Type

	// Return the max size, in bytes, of the value that can be
	// associated with this name. The value may be zero, implying that
	// only the name is stored and that there is no associated value.
	MaxSize() uint64

	// Return the point in the global name space, if any, where this
	// SubTree instance is mounted. If not mounted then return ""
	Root() string
}

var (
	ErrAlreadyMounted = errors.New("Mountpoint cannot be mounted in multiple points in the namespace or on itself")
	ErrNotMounted     = errors.New("Mountpoint not mounted")
	ErrNotSupported   = errors.New("Requested mount operation not yet supported")
)

type MountProtocol interface {
	// Return the point in the global name space, if any, where this
	// MountProtocol instance is mounted.  If not mounted then return ""
	Root() string

	// Get will find the SubTree responsible for the name
	// specified. The name only has to exist somewhere in that
	// SubTree's portion of the name space. The closest enclosing
	// SubTree is returned.
	GetSubTree(name string) (SubTree, error)

	// Mount a SubTree to the existing namespace. More recently
	// mounted subtree's obscure existing ones.
	Mount(subtree SubTree) error

	// CreateSubTree creates a SubTree instance, using the specified
	// options to decide what kind of SubTree to create. Once a SubTree
	// is created. The SubTree will be mounted on the specified root.
	// Root may be relative to this MountProtocol's root, or absolute.
	CreateSubTree(root, options string) (SubTree, error)
}
